<script>
export default {
  components: {},
  data () {
    return {
      radio: 0,
      radio2: 0,
      activeName: '1',
      tableData: [{
        type : 'SQL 注入',
        explanation: '最常见的注入形式，通过恶意拼接数据库语句，来实现非预期的功能'
      },{
        type : '系统命令注入',
        explanation: '通过拼接来执行非预期的操作系统指令'
      },{
        type : '表达式语言注入',
        explanation: 'Java 中常见的命令注入执行方式'
      },{
        type : '服务端模板注入',
        explanation: '使用模板引擎的语言常见的注入形式'
      },],
    };
  },
  computed: {
    isButtonDisabled() {
      return this.radio === 0;
    },
    isButtonDisabled2() {
      return this.radio2 === 0;
    }
  },
  methods: {
    check(radio) {
      console.log(radio)
      if (radio === 3) {
        this.$message.success('恭喜你，答对啦！')
      } else {
        this.$message.error('很遗憾，答错啦！')
      }
    },
    check2(radio2) {
      console.log(radio2)
      if (radio2 === 3) {
        this.$message.success('恭喜你，答对啦！')
      } else {
        this.$message.error('很遗憾，答错啦！')
      }
    },
  }
}
</script>

<template>
  <div>
    <h1 align="center">1.5 逆向工程</h1>
    <el-divider></el-divider>
    <h3>1.5.1 glibc malloc</h3>
    <b>glibc</b><br>
    glibc 即 GNU C Library，是为 GNU 操作系统开发的一个 C 标准库。glibc 主要由两部分组成，一部分是头文件，位于 /usr/include；另一部分是库的二进制文件。二进制文件部分主要是 C 语言标准库，有动态和静态两个版本，动态版本位于 /lib/libc.so.6，静态版本位于 /usr/lib/libc.a。
    <br>
    这一章中，我们将阅读分析 glibc 的源码，下面先把它下载下来，并切换到我们需要的版本：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">$ git clone git</span><span class="pun">:</span><span class="com">//sourceware.org/git/glibc.git</span></code></li><li class="L1"><code class="lang-text"><span class="pln">$ cd glibc</span></code></li><li class="L2"><code class="lang-text"><span class="pln">$ git checkout </span><span class="pun">--</span><span class="pln">track </span><span class="pun">-</span><span class="pln">b local_glibc</span><span class="pun">-</span><span class="lit">2.23</span><span class="pln"> origin</span><span class="pun">/</span><span class="pln">release</span><span class="pun">/</span><span class="lit">2.23</span><span class="pun">/</span><span class="pln">master</span></code></li></ol></pre>
    下面来编译它，首先修改配置文件 Makeconfig，将 -Werror 注释掉，这样可以避免高版本 GCC（v8.1.0） 将警告当做错误处理：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">$ cat </span><span class="typ">Makeconfig</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> grep </span><span class="pun">-</span><span class="pln">i werror </span><span class="pun">|</span><span class="pln"> grep warn</span></code></li><li class="L1"><code class="lang-text"><span class="pun">+</span><span class="pln">gccwarn </span><span class="pun">+=</span><span class="pln"> </span><span class="com">#-Werror</span></code></li></ol></pre>
    接下来需要打上一个 patch：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-diff"><span class="pln">$ cat regexp</span><span class="pun">.</span><span class="pln">patch</span></code></li><li class="L1"><code class="lang-diff"><span class="pln">diff </span><span class="pun">--</span><span class="pln">git a</span><span class="pun">/</span><span class="pln">misc</span><span class="pun">/</span><span class="pln">regexp</span><span class="pun">.</span><span class="pln">c b</span><span class="pun">/</span><span class="pln">misc</span><span class="pun">/</span><span class="pln">regexp</span><span class="pun">.</span><span class="pln">c</span></code></li><li class="L2"><code class="lang-diff"><span class="pln">index </span><span class="lit">19d76c0.</span><span class="pun">.</span><span class="lit">9017bc1</span><span class="pln"> </span><span class="lit">100644</span></code></li><li class="L3"><code class="lang-diff"><span class="pun">---</span><span class="pln"> a</span><span class="pun">/</span><span class="pln">misc</span><span class="pun">/</span><span class="pln">regexp</span><span class="pun">.</span><span class="pln">c</span></code></li><li class="L4"><code class="lang-diff"><span class="pun">+++</span><span class="pln"> b</span><span class="pun">/</span><span class="pln">misc</span><span class="pun">/</span><span class="pln">regexp</span><span class="pun">.</span><span class="pln">c</span></code></li><li class="L5"><code class="lang-diff"><span class="pun">@@</span><span class="pln"> </span><span class="pun">-</span><span class="lit">29</span><span class="pun">,</span><span class="lit">14</span><span class="pln"> </span><span class="pun">+</span><span class="lit">29</span><span class="pun">,</span><span class="lit">17</span><span class="pln"> </span><span class="pun">@@</span></code></li><li class="L6"><code class="lang-diff"></code></li><li class="L7"><code class="lang-diff"><span class="pln"> </span><span class="com">#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_23)</span></code></li><li class="L8"><code class="lang-diff"></code></li><li class="L9"><code class="lang-diff"><span class="pun">-</span><span class="com">/* Define the variables used for the interface.  */</span></code></li><li class="L0"><code class="lang-diff"><span class="pun">-</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">loc1</span><span class="pun">;</span></code></li><li class="L1"><code class="lang-diff"><span class="pun">-</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">loc2</span><span class="pun">;</span></code></li><li class="L2"><code class="lang-diff"><span class="pun">+#</span><span class="pln">include </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">    </span><span class="com">/* Get NULL.  */</span></code></li><li class="L3"><code class="lang-diff"><span class="pun">+</span></code></li><li class="L4"><code class="lang-diff"><span class="pun">+</span><span class="com">/* Define the variables used for the interface.  Avoid .symver on common</span></code></li><li class="L5"><code class="lang-diff"><span class="com">+   symbol, which just creates a new common symbol, not an alias.  */</span></code></li><li class="L6"><code class="lang-diff"><span class="pun">+</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">loc1 </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span></code></li><li class="L7"><code class="lang-diff"><span class="pun">+</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">loc2 </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span></code></li><li class="L8"><code class="lang-diff"><span class="pln"> compat_symbol </span><span class="pun">(</span><span class="pln">libc</span><span class="pun">,</span><span class="pln"> loc1</span><span class="pun">,</span><span class="pln"> loc1</span><span class="pun">,</span><span class="pln"> GLIBC_2_0</span><span class="pun">);</span></code></li><li class="L9"><code class="lang-diff"><span class="pln"> compat_symbol </span><span class="pun">(</span><span class="pln">libc</span><span class="pun">,</span><span class="pln"> loc2</span><span class="pun">,</span><span class="pln"> loc2</span><span class="pun">,</span><span class="pln"> GLIBC_2_0</span><span class="pun">);</span></code></li><li class="L0"><code class="lang-diff"></code></li><li class="L1"><code class="lang-diff"><span class="pln"> </span><span class="com">/* Although we do not support the use we define this variable as well.  */</span></code></li><li class="L2"><code class="lang-diff"><span class="pun">-</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">locs</span><span class="pun">;</span></code></li><li class="L3"><code class="lang-diff"><span class="pun">+</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">locs </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span></code></li><li class="L4"><code class="lang-diff"><span class="pln"> compat_symbol </span><span class="pun">(</span><span class="pln">libc</span><span class="pun">,</span><span class="pln"> locs</span><span class="pun">,</span><span class="pln"> locs</span><span class="pun">,</span><span class="pln"> GLIBC_2_0</span><span class="pun">);</span></code></li><li class="L5"><code class="lang-diff"><span class="pln">$ patch misc</span><span class="pun">/</span><span class="pln">regexp</span><span class="pun">.</span><span class="pln">c regexp</span><span class="pun">.</span><span class="pln">patch</span></code></li></ol></pre>
    然后就可以编译了：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">$ mkdir build </span><span class="pun">&amp;&amp;</span><span class="pln"> cd build</span></code></li><li class="L1"><code class="lang-text"><span class="pln">$ </span><span class="pun">../</span><span class="pln">configure </span><span class="pun">--</span><span class="pln">prefix</span><span class="pun">=</span><span class="str">/usr/</span><span class="kwd">local</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.23</span></code></li><li class="L2"><code class="lang-text"><span class="pln">$ make </span><span class="pun">-</span><span class="pln">j4 </span><span class="pun">&amp;&amp;</span><span class="pln"> sudo make install</span></code></li></ol></pre>
    如果我们想要在编译程序时指定 libc，可以像这样：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">$ gcc </span><span class="pun">-</span><span class="pln">L</span><span class="pun">/</span><span class="pln">usr</span><span class="pun">/</span><span class="kwd">local</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.23</span><span class="pun">/</span><span class="pln">lib </span><span class="pun">-</span><span class="typ">Wl</span><span class="pun">,--</span><span class="pln">rpath</span><span class="pun">=</span><span class="str">/usr/</span><span class="kwd">local</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.23</span><span class="pun">/</span><span class="pln">lib </span><span class="pun">-</span><span class="typ">Wl</span><span class="pun">,-</span><span class="pln">I</span><span class="pun">/</span><span class="pln">usr</span><span class="pun">/</span><span class="kwd">local</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.23</span><span class="pun">/</span><span class="pln">lib</span><span class="pun">/</span><span class="pln">ld</span><span class="pun">-</span><span class="lit">2.23</span><span class="pun">.</span><span class="pln">so test</span><span class="pun">.</span><span class="pln">c</span></code></li><li class="L1"><code class="lang-text"><span class="pln">$ ldd a</span><span class="pun">.</span><span class="kwd">out</span></code></li><li class="L2"><code class="lang-text"><span class="pln">        linux</span><span class="pun">-</span><span class="pln">vdso</span><span class="pun">.</span><span class="pln">so</span><span class="pun">.</span><span class="lit">1</span><span class="pln"> </span><span class="pun">(</span><span class="lit">0x00007ffcc76b0000</span><span class="pun">)</span></code></li><li class="L3"><code class="lang-text"><span class="pln">        libc</span><span class="pun">.</span><span class="pln">so</span><span class="pun">.</span><span class="lit">6</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">/</span><span class="pln">usr</span><span class="pun">/</span><span class="kwd">local</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.23</span><span class="pun">/</span><span class="pln">lib</span><span class="pun">/</span><span class="pln">libc</span><span class="pun">.</span><span class="pln">so</span><span class="pun">.</span><span class="lit">6</span><span class="pln"> </span><span class="pun">(</span><span class="lit">0x00007f6abd578000</span><span class="pun">)</span></code></li><li class="L4"><code class="lang-text"><span class="pln">        </span><span class="pun">/</span><span class="pln">usr</span><span class="pun">/</span><span class="kwd">local</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.23</span><span class="pun">/</span><span class="pln">lib</span><span class="pun">/</span><span class="pln">ld</span><span class="pun">-</span><span class="lit">2.23</span><span class="pun">.</span><span class="pln">so </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">/</span><span class="pln">usr</span><span class="pun">/</span><span class="pln">lib64</span><span class="pun">/</span><span class="pln">ld</span><span class="pun">-</span><span class="pln">linux</span><span class="pun">-</span><span class="pln">x86</span><span class="pun">-</span><span class="lit">64.so</span><span class="pun">.</span><span class="lit">2</span><span class="pln"> </span><span class="pun">(</span><span class="lit">0x00007f6abdb1c000</span><span class="pun">)</span></code></li></ol></pre>
    然后如果希望在调试时指定 libc 的源文件，可以使用 gdb 命令 directory，但是这种方法的缺点是不能解析子目录，所以推荐使用下面的命令在启动时加载：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">gdb </span><span class="str">`find ~/path/to/glibc/source -type d -printf '-d %p '`</span><span class="pln"> </span><span class="pun">./</span><span class="pln">a</span><span class="pun">.</span><span class="kwd">out</span></code></li></ol></pre>
    <b>malloc.c</b><br>
    下面我们先分析 glibc 2.23 版本的源码，它是 Ubuntu16.04 的默认版本，在 pwn 中也最常见。然后，我们再探讨新版本的 glibc 中所加入的漏洞缓解机制。
    <br><b>相关结构</b><br>
    <b>堆块结构</b>
    <ul>
      <li style="margin-left: 2em;">Allocated Chunk</li>
      <li style="margin-left: 2em;">Free Chunk</li>
      <li style="margin-left: 2em;">Top Chunk</li>
    </ul>
    <b>Bins 结构</b>
    <ul>
      <li style="margin-left: 2em;">Fast Bins</li>
      <li style="margin-left: 2em;">Small Bins</li>
      <li style="margin-left: 2em;">Large Bins</li>
      <li style="margin-left: 2em;">Unsorted Bins</li>
    </ul>
    <b>Arena 结构</b>
    <br><b>分配函数</b><br>
    _int_malloc()
    <br><b>释放函数</b><br>
    _int_free()
    <br><b>重分配函数</b><br>
    _int_realloc()
    <br><b>参考资料</b>
    <ul>
      <li style="margin-left: 2em;"><a href="https://www.gnu.org/software/libc/" target="_blank">The GNU C Library (glibc)</a></li>
      <li style="margin-left: 2em;"><a href="https://www.gnu.org/software/libc/manual/" target="_blank">glibc manual</a></li>
    </ul>

    <el-divider></el-divider>
    <div class="bottom-container">
      <h3>下面哪个选项不是堆块结构？</h3>
      <el-radio-group v-model="radio">
        <el-radio :label="1">Allocated Chunk</el-radio>
        <el-radio :label="2">Free Chunk</el-radio>
        <el-radio :label="3">Fast Bins</el-radio>
        <el-radio :label="4">Top Chunk</el-radio>
        <el-button type="primary" plain :disabled="isButtonDisabled" @click="check(radio)">检查</el-button>
      </el-radio-group>
    </div>
    <el-divider></el-divider>
    <h3>1.5.2 Linux 内核</h3>
    <b>编译安装</b>
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">$ uname </span><span class="pun">-</span><span class="pln">a</span></code></li><li class="L1"><code class="lang-text"><span class="typ">Linux</span><span class="pln"> firmy</span><span class="pun">-</span><span class="pln">pc </span><span class="lit">4.14</span><span class="pun">.</span><span class="lit">34</span><span class="pun">-</span><span class="lit">1</span><span class="pun">-</span><span class="pln">MANJARO </span><span class="com">#1 SMP PREEMPT Thu Apr 12 17:26:43 UTC 2018 x86_64 GNU/Linux</span></code></li><li class="L2"><code class="lang-text"><span class="pln">$ yaourt </span><span class="pun">-</span><span class="pln">S </span><span class="kwd">base</span><span class="pun">-</span><span class="pln">devel</span></code></li></ol></pre>
    为了方便学习，选择一个稳定版本，比如最新的 4.16.3。
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">$ mkdir </span><span class="pun">~</span><span class="str">/kernelbuild &amp;&amp; cd ~/</span><span class="pln">kernelbuild</span></code></li><li class="L1"><code class="lang-text"><span class="pln">$ wget </span><span class="pun">-</span><span class="pln">c https</span><span class="pun">:</span><span class="com">//cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.16.3.tar.xz</span></code></li><li class="L2"><code class="lang-text"><span class="pln">$ tar </span><span class="pun">-</span><span class="pln">xvJf linux</span><span class="pun">-</span><span class="lit">4.16</span><span class="pun">.</span><span class="lit">3.tar</span><span class="pun">.</span><span class="pln">xz</span></code></li><li class="L3"><code class="lang-text"><span class="pln">$ cd linux</span><span class="pun">-</span><span class="lit">4.16</span><span class="pun">.</span><span class="lit">3</span><span class="pun">/</span></code></li><li class="L4"><code class="lang-text"><span class="pln">$ make clean </span><span class="pun">&amp;&amp;</span><span class="pln"> make mrproper</span></code></li></ol></pre>
    内核的配置选项在 .config 文件中，有两种方法可以设置这些选项，一种是从当前内核中获得一份默认配置：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">$ zcat </span><span class="pun">/</span><span class="pln">proc</span><span class="pun">/</span><span class="pln">config</span><span class="pun">.</span><span class="pln">gz </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln">config</span></code></li><li class="L1"><code class="lang-text"><span class="pln">$ make oldconfig</span></code></li></ol></pre>
    另一种是自己生成一份配置：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">$ make localmodconfig   </span><span class="com"># 使用当前内核配置生成</span></code></li><li class="L1"><code class="lang-text"><span class="pln">    </span><span class="com"># OR</span></code></li><li class="L2"><code class="lang-text"><span class="pln">$ make defconfig        </span><span class="com"># 根据当前架构默认的配置生成</span></code></li></ol></pre>
    为了能够对内核进行调试，需要设置下面的参数：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">CONFIG_DEBUG_INFO</span><span class="pun">=</span><span class="pln">y</span></code></li><li class="L1"><code class="lang-text"><span class="pln">CONFIG_DEBUG_INFO_REDUCED</span><span class="pun">=</span><span class="pln">n</span></code></li><li class="L2"><code class="lang-text"><span class="pln">CONFIG_GDB_SCRIPTS</span><span class="pun">=</span><span class="pln">y</span></code></li></ol></pre>
    如果需要使用 kgdb，还需要开启下面的参数：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">CONFIG_STRICT_KERNEL_RWX</span><span class="pun">=</span><span class="pln">n</span></code></li><li class="L1"><code class="lang-text"><span class="pln">CONFIG_FRAME_POINTER</span><span class="pun">=</span><span class="pln">y</span></code></li><li class="L2"><code class="lang-text"><span class="pln">CONFIG_KGDB</span><span class="pun">=</span><span class="pln">y</span></code></li><li class="L3"><code class="lang-text"><span class="pln">CONFIG_KGDB_SERIAL_CONSOLE</span><span class="pun">=</span><span class="pln">y</span></code></li></ol></pre>
    CONFIG_STRICT_KERNEL_RWX 会将特定的内核内存空间标记为只读，这将阻止你使用软件断点，最好将它关掉。如果希望使用 kdb，在上面的基础上再加上：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">CONFIG_KGDB_KDB</span><span class="pun">=</span><span class="pln">y</span></code></li><li class="L1"><code class="lang-text"><span class="pln">CONFIG_KDB_KEYBOARD</span><span class="pun">=</span><span class="pln">y</span></code></li></ol></pre>
    另外如果你在调试时不希望被 KASLR 干扰，可以在编译时关掉它：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">CONFIG_RANDOMIZE_BASE</span><span class="pun">=</span><span class="pln">n</span></code></li><li class="L1"><code class="lang-text"><span class="pln">CONFIG_RANDOMIZE_MEMORY</span><span class="pun">=</span><span class="pln">n</span></code></li></ol></pre>
    将上面的参数写到文件 .config-fragment，然后合并进 .config：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">$ </span><span class="pun">./</span><span class="pln">scripts</span><span class="pun">/</span><span class="pln">kconfig</span><span class="pun">/</span><span class="pln">merge_config</span><span class="pun">.</span><span class="pln">sh </span><span class="pun">.</span><span class="pln">config </span><span class="pun">.</span><span class="pln">config</span><span class="pun">-</span><span class="pln">fragment</span></code></li></ol></pre>
    最后因为内核编译默认开启了 -O2 优化，可以修改 Makefile 为 -O0：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">KBUILD_CFLAGS   </span><span class="pun">+=</span><span class="pln"> </span><span class="pun">-</span><span class="pln">O0</span></code></li></ol></pre>
    编译内核：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">$ make</span></code></li></ol></pre>
    完成后当然就是安装，但我们这里并不是真的要将本机的内核换掉，接下来的过程就交给 QEMU 了。（参考章节4.1）
    <br><b>系统调用</b><br>
    在 Linux 中，系统调用是一些内核空间函数，是用户空间访问内核的唯一手段。这些函数与 CPU 架构有关，x86-64 架构提供了 322 个系统调用，x86 提供了 358 个系统调用
    <br>
    下面是一个用 32 位汇编写的例子，源码：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pun">.</span><span class="pln">data</span></code></li><li class="L1"><code class="lang-text"></code></li><li class="L2"><code class="lang-text"><span class="pln">msg</span><span class="pun">:</span></code></li><li class="L3"><code class="lang-text"><span class="pln">    </span><span class="pun">.</span><span class="pln">ascii </span><span class="str">"hello 32-bit!\n"</span></code></li><li class="L4"><code class="lang-text"><span class="pln">    len </span><span class="pun">=</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> msg</span></code></li><li class="L5"><code class="lang-text"></code></li><li class="L6"><code class="lang-text"><span class="pun">.</span><span class="pln">text</span></code></li><li class="L7"><code class="lang-text"><span class="pln">    </span><span class="pun">.</span><span class="kwd">global</span><span class="pln"> _start</span></code></li><li class="L8"><code class="lang-text"></code></li><li class="L9"><code class="lang-text"><span class="pln">_start</span><span class="pun">:</span></code></li><li class="L0"><code class="lang-text"><span class="pln">    movl $len</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">edx</span></code></li><li class="L1"><code class="lang-text"><span class="pln">    movl $msg</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">ecx</span></code></li><li class="L2"><code class="lang-text"><span class="pln">    movl $1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">ebx</span></code></li><li class="L3"><code class="lang-text"><span class="pln">    movl $4</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">eax</span></code></li><li class="L4"><code class="lang-text"><span class="pln">    </span><span class="kwd">int</span><span class="pln"> $0x80</span></code></li><li class="L5"><code class="lang-text"></code></li><li class="L6"><code class="lang-text"><span class="pln">    movl $0</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">ebx</span></code></li><li class="L7"><code class="lang-text"><span class="pln">    movl $1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">eax</span></code></li><li class="L8"><code class="lang-text"><span class="pln">    </span><span class="kwd">int</span><span class="pln"> $0x80</span></code></li></ol></pre>
    编译执行（可以编译成64位程序的）：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">$ gcc </span><span class="pun">-</span><span class="pln">m32 </span><span class="pun">-</span><span class="pln">c hello32</span><span class="pun">.</span><span class="pln">S</span></code></li><li class="L1"><code class="lang-text"><span class="pln">$ ld </span><span class="pun">-</span><span class="pln">m elf_i386 </span><span class="pun">-</span><span class="pln">o hello32 hello32</span><span class="pun">.</span><span class="pln">o</span></code></li><li class="L2"><code class="lang-text"><span class="pln">$ strace </span><span class="pun">./</span><span class="pln">hello32</span></code></li><li class="L3"><code class="lang-text"><span class="pln">execve</span><span class="pun">(</span><span class="str">"./hello32"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="str">"./hello32"</span><span class="pun">],</span><span class="pln"> </span><span class="lit">0x7ffff990f830</span><span class="pln"> </span><span class="com">/* 68 vars */</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span></code></li><li class="L4"><code class="lang-text"><span class="pln">strace</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> </span><span class="typ">Process</span><span class="pln"> PID</span><span class="pun">=</span><span class="lit">19355</span><span class="pln"> runs </span><span class="kwd">in</span><span class="pln"> </span><span class="lit">32</span><span class="pln"> bit mode</span><span class="pun">.</span><span class="pln"> </span><span class="pun">]</span></code></li><li class="L5"><code class="lang-text"><span class="pln">write</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">"hello 32-bit!\n"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">14hello</span><span class="pln"> </span><span class="lit">32</span><span class="pun">-</span><span class="pln">bit</span><span class="pun">!</span></code></li><li class="L6"><code class="lang-text"><span class="pun">)</span><span class="pln">         </span><span class="pun">=</span><span class="pln"> </span><span class="lit">14</span></code></li><li class="L7"><code class="lang-text"><span class="kwd">exit</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">                                 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">?</span></code></li><li class="L8"><code class="lang-text"><span class="pun">+++</span><span class="pln"> exited </span><span class="kwd">with</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">+++</span></code></li></ol></pre>
    可以看到程序将调用号保存到 eax，并通过 int $0x80 来使用系统调用。
    <br>
    虽然软中断 int 0x80 非常经典，早期 2.6 及以前版本的内核都使用这种机制进行系统调用。但因其性能较差，在往后的内核中使用了快速系统调用指令来替代，32 位系统使用 sysenter（对应sysexit） 指令，而 64 位系统使用 syscall（对应sysret） 指令。
    <br>
    一个使用 sysenter 的例子：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pun">.</span><span class="pln">data</span></code></li><li class="L1"><code class="lang-text"></code></li><li class="L2"><code class="lang-text"><span class="pln">msg</span><span class="pun">:</span></code></li><li class="L3"><code class="lang-text"><span class="pln">    </span><span class="pun">.</span><span class="pln">ascii </span><span class="str">"Hello sysenter!\n"</span></code></li><li class="L4"><code class="lang-text"><span class="pln">    len </span><span class="pun">=</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> msg</span></code></li><li class="L5"><code class="lang-text"></code></li><li class="L6"><code class="lang-text"><span class="pun">.</span><span class="pln">text</span></code></li><li class="L7"><code class="lang-text"><span class="pln">    </span><span class="pun">.</span><span class="pln">globl _start</span></code></li><li class="L8"><code class="lang-text"></code></li><li class="L9"><code class="lang-text"><span class="pln">_start</span><span class="pun">:</span></code></li><li class="L0"><code class="lang-text"><span class="pln">    movl $len</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">edx</span></code></li><li class="L1"><code class="lang-text"><span class="pln">    movl $msg</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">ecx</span></code></li><li class="L2"><code class="lang-text"><span class="pln">    movl $1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">ebx</span></code></li><li class="L3"><code class="lang-text"><span class="pln">    movl $4</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">eax</span></code></li><li class="L4"><code class="lang-text"><span class="pln">    </span><span class="com"># Setting the stack for the systenter</span></code></li><li class="L5"><code class="lang-text"><span class="pln">    pushl $sysenter_ret</span></code></li><li class="L6"><code class="lang-text"><span class="pln">    pushl </span><span class="pun">%</span><span class="pln">ecx</span></code></li><li class="L7"><code class="lang-text"><span class="pln">    pushl </span><span class="pun">%</span><span class="pln">edx</span></code></li><li class="L8"><code class="lang-text"><span class="pln">    pushl </span><span class="pun">%</span><span class="pln">ebp</span></code></li><li class="L9"><code class="lang-text"><span class="pln">    movl </span><span class="pun">%</span><span class="pln">esp</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">ebp</span></code></li><li class="L0"><code class="lang-text"><span class="pln">    sysenter</span></code></li><li class="L1"><code class="lang-text"></code></li><li class="L2"><code class="lang-text"><span class="pln">sysenter_ret</span><span class="pun">:</span></code></li><li class="L3"><code class="lang-text"><span class="pln">    movl $0</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">ebx</span></code></li><li class="L4"><code class="lang-text"><span class="pln">    movl $1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">eax</span></code></li><li class="L5"><code class="lang-text"><span class="pln">    </span><span class="com"># Setting the stack for the systenter</span></code></li><li class="L6"><code class="lang-text"><span class="pln">    pushl $sysenter_ret</span></code></li><li class="L7"><code class="lang-text"><span class="pln">    pushl </span><span class="pun">%</span><span class="pln">ecx</span></code></li><li class="L8"><code class="lang-text"><span class="pln">    pushl </span><span class="pun">%</span><span class="pln">edx</span></code></li><li class="L9"><code class="lang-text"><span class="pln">    pushl </span><span class="pun">%</span><span class="pln">ebp</span></code></li><li class="L0"><code class="lang-text"><span class="pln">    movl </span><span class="pun">%</span><span class="pln">esp</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">ebp</span></code></li><li class="L1"><code class="lang-text"><span class="pln">    sysenter</span></code></li></ol></pre>
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">$ gcc </span><span class="pun">-</span><span class="pln">m32 </span><span class="pun">-</span><span class="pln">c sysenter</span><span class="pun">.</span><span class="pln">S</span></code></li><li class="L1"><code class="lang-text"><span class="pln">$ ld </span><span class="pun">-</span><span class="pln">m elf_i386 </span><span class="pun">-</span><span class="pln">o sysenter sysenter</span><span class="pun">.</span><span class="pln">o</span></code></li><li class="L2"><code class="lang-text"><span class="pln">$ strace </span><span class="pun">./</span><span class="pln">sysenter</span></code></li><li class="L3"><code class="lang-text"><span class="pln">execve</span><span class="pun">(</span><span class="str">"./sysenter"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="str">"./sysenter"</span><span class="pun">],</span><span class="pln"> </span><span class="lit">0x7fff73993fd0</span><span class="pln"> </span><span class="com">/* 69 vars */</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span></code></li><li class="L4"><code class="lang-text"><span class="pln">strace</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> </span><span class="typ">Process</span><span class="pln"> PID</span><span class="pun">=</span><span class="lit">7663</span><span class="pln"> runs </span><span class="kwd">in</span><span class="pln"> </span><span class="lit">32</span><span class="pln"> bit mode</span><span class="pun">.</span><span class="pln"> </span><span class="pun">]</span></code></li><li class="L5"><code class="lang-text"><span class="pln">write</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Hello sysenter!\n"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16Hello</span><span class="pln"> sysenter</span><span class="pun">!</span></code></li><li class="L6"><code class="lang-text"><span class="pun">)</span><span class="pln">       </span><span class="pun">=</span><span class="pln"> </span><span class="lit">16</span></code></li><li class="L7"><code class="lang-text"><span class="kwd">exit</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">                                 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">?</span></code></li><li class="L8"><code class="lang-text"><span class="pun">+++</span><span class="pln"> exited </span><span class="kwd">with</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">+++</span></code></li></ol></pre>
    可以看到，为了使用 sysenter 指令，需要为其手动布置栈。这是因为在 sysenter 返回时，会执行 __kernel_vsyscall 的后半部分（从0xf7fd5059开始）：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">gdb</span><span class="pun">-</span><span class="pln">peda$ vmmap vdso</span></code></li><li class="L1"><code class="lang-text"><span class="typ">Start</span><span class="pln">      </span><span class="typ">End</span><span class="pln">        </span><span class="typ">Perm</span><span class="pln">      </span><span class="typ">Name</span></code></li><li class="L2"><code class="lang-text"><span class="lit">0xf7fd4000</span><span class="pln"> </span><span class="lit">0xf7fd6000</span><span class="pln"> r</span><span class="pun">-</span><span class="pln">xp      </span><span class="pun">[</span><span class="pln">vdso</span><span class="pun">]</span></code></li><li class="L3"><code class="lang-text"><span class="pln">gdb</span><span class="pun">-</span><span class="pln">peda$ disassemble __kernel_vsyscall</span></code></li><li class="L4"><code class="lang-text"><span class="typ">Dump</span><span class="pln"> of assembler code </span><span class="kwd">for</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> __kernel_vsyscall</span><span class="pun">:</span></code></li><li class="L5"><code class="lang-text"><span class="pln">   </span><span class="lit">0xf7fd5050</span><span class="pln"> </span><span class="pun">&lt;+</span><span class="lit">0</span><span class="pun">&gt;:</span><span class="pln">     push   ecx</span></code></li><li class="L6"><code class="lang-text"><span class="pln">   </span><span class="lit">0xf7fd5051</span><span class="pln"> </span><span class="pun">&lt;+</span><span class="lit">1</span><span class="pun">&gt;:</span><span class="pln">     push   edx</span></code></li><li class="L7"><code class="lang-text"><span class="pln">   </span><span class="lit">0xf7fd5052</span><span class="pln"> </span><span class="pun">&lt;+</span><span class="lit">2</span><span class="pun">&gt;:</span><span class="pln">     push   ebp</span></code></li><li class="L8"><code class="lang-text"><span class="pln">   </span><span class="lit">0xf7fd5053</span><span class="pln"> </span><span class="pun">&lt;+</span><span class="lit">3</span><span class="pun">&gt;:</span><span class="pln">     mov    ebp</span><span class="pun">,</span><span class="pln">esp</span></code></li><li class="L9"><code class="lang-text"><span class="pln">   </span><span class="lit">0xf7fd5055</span><span class="pln"> </span><span class="pun">&lt;+</span><span class="lit">5</span><span class="pun">&gt;:</span><span class="pln">     sysenter</span></code></li><li class="L0"><code class="lang-text"><span class="pln">   </span><span class="lit">0xf7fd5057</span><span class="pln"> </span><span class="pun">&lt;+</span><span class="lit">7</span><span class="pun">&gt;:</span><span class="pln">     </span><span class="kwd">int</span><span class="pln">    </span><span class="lit">0x80</span></code></li><li class="L1"><code class="lang-text"><span class="pln">   </span><span class="lit">0xf7fd5059</span><span class="pln"> </span><span class="pun">&lt;+</span><span class="lit">9</span><span class="pun">&gt;:</span><span class="pln">     pop    ebp</span></code></li><li class="L2"><code class="lang-text"><span class="pln">   </span><span class="lit">0xf7fd505a</span><span class="pln"> </span><span class="pun">&lt;+</span><span class="lit">10</span><span class="pun">&gt;:</span><span class="pln">    pop    edx</span></code></li><li class="L3"><code class="lang-text"><span class="pln">   </span><span class="lit">0xf7fd505b</span><span class="pln"> </span><span class="pun">&lt;+</span><span class="lit">11</span><span class="pun">&gt;:</span><span class="pln">    pop    ecx</span></code></li><li class="L4"><code class="lang-text"><span class="pln">   </span><span class="lit">0xf7fd505c</span><span class="pln"> </span><span class="pun">&lt;+</span><span class="lit">12</span><span class="pun">&gt;:</span><span class="pln">    ret</span></code></li><li class="L5"><code class="lang-text"><span class="typ">End</span><span class="pln"> of assembler </span><span class="kwd">dump</span><span class="pun">.</span></code></li></ol></pre>
    __kernel_vsyscall 封装了 sysenter 调用的规范，是 vDSO 的一部分，而 vDSO 允许程序在用户层中执行内核代码。关于 vDSO 的内容我们将在后面的章节中细讲。
    <br>
    下面是一个 64 位使用 syscall 的例子：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pun">.</span><span class="pln">data</span></code></li><li class="L1"><code class="lang-text"></code></li><li class="L2"><code class="lang-text"><span class="pln">msg</span><span class="pun">:</span></code></li><li class="L3"><code class="lang-text"><span class="pln">    </span><span class="pun">.</span><span class="pln">ascii </span><span class="str">"Hello 64-bit!\n"</span></code></li><li class="L4"><code class="lang-text"><span class="pln">    len </span><span class="pun">=</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> msg</span></code></li><li class="L5"><code class="lang-text"></code></li><li class="L6"><code class="lang-text"><span class="pun">.</span><span class="pln">text</span></code></li><li class="L7"><code class="lang-text"><span class="pln">    </span><span class="pun">.</span><span class="kwd">global</span><span class="pln"> _start</span></code></li><li class="L8"><code class="lang-text"></code></li><li class="L9"><code class="lang-text"><span class="pln">_start</span><span class="pun">:</span></code></li><li class="L0"><code class="lang-text"><span class="pln">    movq  $1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">rdi</span></code></li><li class="L1"><code class="lang-text"><span class="pln">    movq  $msg</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">rsi</span></code></li><li class="L2"><code class="lang-text"><span class="pln">    movq  $len</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">rdx</span></code></li><li class="L3"><code class="lang-text"><span class="pln">    movq  $1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">rax</span></code></li><li class="L4"><code class="lang-text"><span class="pln">    syscall</span></code></li><li class="L5"><code class="lang-text"></code></li><li class="L6"><code class="lang-text"><span class="pln">    xorq  </span><span class="pun">%</span><span class="pln">rdi</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">rdi</span></code></li><li class="L7"><code class="lang-text"><span class="pln">    movq  $60</span><span class="pun">,</span><span class="pln"> </span><span class="pun">%</span><span class="pln">rax</span></code></li><li class="L8"><code class="lang-text"><span class="pln">    syscall</span></code></li></ol></pre>
    编译执行（不能编译成32位程序）：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pln">$ gcc </span><span class="pun">-</span><span class="pln">c hello64</span><span class="pun">.</span><span class="pln">S</span></code></li><li class="L1"><code class="lang-text"><span class="pln">$ ld </span><span class="pun">-</span><span class="pln">o hello64 hello64</span><span class="pun">.</span><span class="pln">o</span></code></li><li class="L2"><code class="lang-text"><span class="pln">$ strace </span><span class="pun">./</span><span class="pln">hello64</span></code></li><li class="L3"><code class="lang-text"><span class="pln">execve</span><span class="pun">(</span><span class="str">"./hello64"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="str">"./hello64"</span><span class="pun">],</span><span class="pln"> </span><span class="lit">0x7ffe11485290</span><span class="pln"> </span><span class="com">/* 68 vars */</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span></code></li><li class="L4"><code class="lang-text"><span class="pln">write</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Hello 64-bit!\n"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">14Hello</span><span class="pln"> </span><span class="lit">64</span><span class="pun">-</span><span class="pln">bit</span><span class="pun">!</span></code></li><li class="L5"><code class="lang-text"><span class="pun">)</span><span class="pln">         </span><span class="pun">=</span><span class="pln"> </span><span class="lit">14</span></code></li><li class="L6"><code class="lang-text"><span class="kwd">exit</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">                                 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">?</span></code></li><li class="L7"><code class="lang-text"><span class="pun">+++</span><span class="pln"> exited </span><span class="kwd">with</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">+++</span></code></li></ol></pre>
    在这两个例子中我们直接使用了 execve、write 和 exit 三个系统调用。但一般情况下，应用程序通过在用户空间实现的应用编程接口（API）而不是直接通过系统调用来编程。例如函数 printf() 的调用过程是这样的：
    <pre style="margin-left: 2em;"><i class="fa fa-copy"></i><ol class="linenums"><li class="L0"><code class="lang-text"><span class="pun">调用</span><span class="pln">printf</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==&gt;</span><span class="pln"> C</span><span class="pun">库中的</span><span class="pln">printf</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==&gt;</span><span class="pln"> C</span><span class="pun">库中的</span><span class="pln">write</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==&gt;</span><span class="pln"> write</span><span class="pun">()系统调用</span></code></li></ol></pre>

    <h3>参考资料</h3>
    <ul>
      <li style="margin-left: 2em;"><a href="https://www.kernel.org/doc/html/latest/" target="_blank">The Linux Kernel documentation</a></li>
      <li style="margin-left: 2em;"><a href="https://legacy.gitbook.com/book/0xax/linux-insides/details" target="_blank">linux-insides</a></li>
    </ul>
    <el-divider></el-divider>


    <div class="bottom-container">
      <h3>在 Linux 内核中，下列哪个子系统负责处理文件系统相关的操作？</h3>
      <el-radio-group v-model="radio2">
        <el-radio :label="1">网络子系统</el-radio>
        <el-radio :label="2">内存管理子系统</el-radio>
        <el-radio :label="3">文件系统子系统</el-radio>
        <el-radio :label="4">进程管理子系统</el-radio>
        <el-button type="primary" plain :disabled="isButtonDisabled2" @click="check2(radio2)">检查</el-button>
      </el-radio-group>
    </div>
    <el-divider></el-divider>

  </div>
</template>

<style scoped>

</style>